package cn.virens.common.mybatis.init;

import cn.virens.common.util.exception.APIException;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.text.StrUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

public class InitExecutor {
    private final InitExecutorFactory executorFactory;

    private Function<String, Integer> sqlExecutor;
    private Consumer<String> outExecutor;

    private List<String> multiline;

    private int result = 0;

    protected InitExecutor(InitExecutorFactory executor) {
        this.executorFactory = executor;
    }

    /**
     * 设置调试执行回调
     */
    public void setOutExecutor(Consumer<String> outExecutor) {
        this.outExecutor = outExecutor;
    }

    /**
     * 设置SQL执行回调
     */
    public void setSqlExecutor(Function<String, Integer> sqlExecutor) {
        this.sqlExecutor = sqlExecutor;
    }

    /**
     * 根据模版文件按行执行数据插入
     *
     * @param map  初始化参数
     * @param path 模板路径
     * @return 执行数量
     */
    public int exec(Map<String, Object> map, String path) throws APIException {
        for (String line : FileUtil.readUtf8Lines(FileUtil.file(path))) {
            if (map != null && execLine0(map, line)) continue;
            if (map != null && execLine1(map, line)) continue;
            if (map != null && execLine2(map, line)) continue;
            if (map != null && execLine3(map, line)) continue;
        }

        return result;
    }

    /**
     * 执行行处理：执行单行 || 添加多行SQL
     */
    protected boolean execLine3(Map<String, Object> map, String line) {
        if (StrUtil.isNotEmpty(line) && !StrUtil.startWith(line, '#')) {
            if (multiline == null || !multiline.add(line)) {
                this.result += exec0(map, line);
            }
        } else {
            return false;
        }

        return true;
    }

    /**
     * 执行行处理：判断是否多行SQL结束点 & 执行多行SQL
     */
    protected boolean execLine2(Map<String, Object> map, String line) {
        if (StrUtil.equals(line, "# SQL-MULTILINE-END")) {
            this.result += exec0(map, multiline);

            this.multiline = null;
        } else {
            return false;
        }

        return true;
    }

    /**
     * 执行行处理：判断是否多行SQL起始点
     */
    protected boolean execLine1(Map<String, Object> map, String line) {
        if (StrUtil.equals(line, "# SQL-MULTILINE-START")) {
            this.multiline = new ArrayList<>();
        } else {
            return false;
        }

        return true;
    }

    /**
     * 执行行处理：判断是否调试打印点
     */
    protected boolean execLine0(Map<String, Object> map, String line) {
        if (StrUtil.startWith(line, "# OUT")) {
            this.outExecutor.accept(line);
        } else {
            return false;
        }

        return true;
    }

    /**
     * 回调执行SQL(多行)
     */
    protected Integer exec0(Map<String, Object> map, List<String> line) {
        return exec0(map, CollUtil.join(line, ""));
    }

    /**
     * 回调执行SQL
     */
    protected Integer exec0(Map<String, Object> map, String line) {
        return sqlExecutor.apply(executorFactory.format(map, line));
    }
}
